﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using System.Windows.Forms;

namespace KinectGame
{
    public class MotionController
    {
        private static MotionController _instance;
        private KinectBaseForm activeWindow; 
        private KinectSetup kinectSetup;
        private KinectSensor sensor; 
        private Skeleton skeleton;
        private bool acceptStart;
        private bool crossStart; 
        private double previous;
        private bool recognised;
        private bool setUpMode;
        private int pointsRecognised;
        private SkeletonPoint leftUp;
        private SkeletonPoint leftDown;
        private SkeletonPoint rightUp;
        private SkeletonPoint rightDown;
        private double verticalDifference; 
        private double horizontalDifference;
        private int horizontalSplit;
        private int verticalSplit;
        private SkeletonPoint activePoint;
        private int horizontalItem;
        private int verticalItem;
        private SkeletonPoint previousPoint;
        private GameContext gameContext; 

        public static MotionController Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new MotionController();
                return _instance;
            }
        }
        
        private MotionController()
        { 
        }

        public void StartSensor(KinectSetup kinectSetup, GameContext gameContext)
        {
            this.gameContext = gameContext; 
            foreach (var potentialSensor in KinectSensor.KinectSensors)
            {
                if (potentialSensor.Status == KinectStatus.Connected)
                {
                    this.sensor = potentialSensor;
                    break;
                }
            }
            if (this.sensor != null)
            {
                this.acceptStart = false;
                this.recognised = false;
                this.setUpMode = true;
                this.pointsRecognised = 0;
                this.kinectSetup = kinectSetup;
                this.kinectSetup.Show();
                this.sensor.SkeletonFrameReady += runtime_SkeletonFrameReady;
                this.sensor.Start();

                this.sensor.ColorStream.Enable();
                this.sensor.SkeletonStream.Enable();
            }
            else
            {
                this.gameContext.ApplicationClose();
            }
        }

        public void SetActiveWindow(KinectBaseForm activeWindow)
        {
            this.activeWindow = activeWindow;
            this.horizontalSplit = this.activeWindow.SetHorizontalItemNumber();
            this.verticalSplit = this.activeWindow.SetVerticalItemNumber();
            //Console.WriteLine("vertical difference " + this.verticalDifference / this.verticalSplit);
            //Console.WriteLine("horizontal difference " + this.horizontalDifference / this.horizontalSplit); 
        }

        private void runtime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            bool receivedData = false;

            Skeleton[] skeletons = null;

            using (SkeletonFrame SFrame = e.OpenSkeletonFrame())
            {
                if (SFrame == null) return;
                skeletons = new Skeleton[SFrame.SkeletonArrayLength];
                SFrame.CopySkeletonDataTo(skeletons);
                receivedData = true;
            }

            if (!receivedData) return;

            if (skeletons == null) return;
            this.skeleton = (from s in skeletons
                             where s.TrackingState == SkeletonTrackingState.Tracked
                             select s).FirstOrDefault();

            if (this.skeleton == null) return;
            if (!recognised)
            {
                recognised = true;
                this.kinectSetup.Click(0,0); 
            }
            if (skeleton.Joints[JointType.HandLeft] == null)
                return;
            SkeletonPoint leftHand = skeleton.Joints[JointType.HandLeft].Position;
            if (skeleton.Joints[JointType.HandRight] == null)
                return;
            SkeletonPoint rightHand = skeleton.Joints[JointType.HandRight].Position;
            if (skeleton.Joints[JointType.Head] == null)
                return;
            SkeletonPoint head = skeleton.Joints[JointType.Head].Position;
            Move(leftHand); 
            HandOverHead(head, rightHand);
            Accept(head, rightHand, leftHand);
            HandCross(leftHand, rightHand);
            HandCrossAccept(leftHand, rightHand); 
        }

        private bool HandOverHead(SkeletonPoint head, SkeletonPoint rightHand)
        {
            if (head.Y > rightHand.Y)
                return false;
            this.previous = rightHand.Y;
            acceptStart = true;
            return true;
        }

        private bool HandCross(SkeletonPoint leftHand, SkeletonPoint rightHand)
        {
            if (leftHand.X < rightHand.X)
                return false;
            this.crossStart = true;
            return true;
        }

        private bool HandCrossAccept(SkeletonPoint leftHand, SkeletonPoint rightHand)
        {
            if (!this.crossStart)
                return false;
            if (leftHand.X > rightHand.X)
                return false;
            this.crossStart = false;
            if(this.activeWindow != null) this.activeWindow.ApplicationSpecificAction(); 
            return true;
        }

        private void Setup(SkeletonPoint leftHand) //setup niech dziedziczy z kinect base i komendy 
        {
            if (this.pointsRecognised == 0)
            {
                this.kinectSetup.Click(0,1);  
                this.leftUp = leftHand;
                this.pointsRecognised = 1;
                //Console.WriteLine("leftUp: " + this.leftUp.X + " " + this.leftUp.Y);
                return;
            }
            if (this.pointsRecognised == 1)
            {
                this.kinectSetup.Click(0, 2); 
                this.leftDown = leftHand;
                this.pointsRecognised = 2;
                //Console.WriteLine("leftDown: " + this.leftDown.X + " " + this.leftDown.Y);
                return;
            }
            if (this.pointsRecognised == 2)
            {
                this.kinectSetup.Click(0, 3); 
                this.rightDown = leftHand;
                this.pointsRecognised = 3;
                //Console.WriteLine("rightDown: " + this.rightDown.X + " " + this.rightDown.Y);
                return;
            }
            if (this.pointsRecognised == 3)
            {
                this.kinectSetup.Click(0,4); 
                this.rightUp = leftHand;
                //Console.WriteLine("rightUp: " + this.rightUp.X + " " + this.rightUp.Y);
                this.pointsRecognised = 4;
                //this.setUpMode = false;
                return;
            }
            if (this.pointsRecognised == 4)
            {
                this.setUpMode = false;
                SetDifferences();
                this.previousPoint = leftHand;
                this.kinectSetup.Close(); 
                this.gameContext.StartMainWindow();
            }
        }

        private void SetDifferences()
        {
            this.verticalDifference = (this.leftUp.Y + this.rightUp.Y - this.leftDown.Y - this.rightDown.Y) / 2;
            this.horizontalDifference = (this.rightDown.X + this.rightUp.X - this.leftDown.X - this.leftUp.X) / 2;
        }

        private void Move(SkeletonPoint leftHand)
        {
            if (this.activeWindow == null) return;
            if (this.setUpMode) return;
            if (this.horizontalSplit > 1)
            {
                if (this.horizontalItem == 0)
                {
                    if (this.activePoint.X > leftHand.X)
                        this.activePoint.X = leftHand.X;
                    else
                        MoveX(leftHand);
                }
                else if (this.horizontalItem == this.horizontalSplit - 1)
                {
                    if (this.activePoint.X < leftHand.X)
                        this.activePoint.X = leftHand.X;
                    else 
                        MoveX(leftHand);
                }
                else
                {
                    MoveX(leftHand); 
                }
            }
            if (this.verticalSplit > 1)
            {
                if (this.verticalItem == 0)
                {
                    if (this.activePoint.Y < leftHand.Y)
                        this.activePoint.Y = leftHand.Y;
                    else
                        MoveY(leftHand);
                }
                else if (this.verticalItem == this.verticalSplit - 1)
                {
                    if (this.activePoint.Y > leftHand.Y)
                        this.activePoint.Y = leftHand.Y;
                    else
                        MoveY(leftHand); 
                }
                else
                {
                    MoveY(leftHand); 
                }
            }
        }

        private void MoveX(SkeletonPoint leftHand)
        {
            if (leftHand.X - this.activePoint.X >= this.horizontalDifference / this.horizontalSplit)
            {
                this.activePoint.X = leftHand.X; 
                this.horizontalItem++;
                this.activeWindow.highlightButton(this.horizontalItem, this.verticalItem);
                //Console.WriteLine("highlight horizontal " + this.horizontalItem + " " + this.verticalItem);
            }
            else if (this.activePoint.X - leftHand.X >= this.horizontalDifference / this.horizontalSplit)
            {
                this.activePoint.X = leftHand.X; 
                this.horizontalItem--;
                this.activeWindow.highlightButton(this.horizontalItem, this.verticalItem);
                //Console.WriteLine("highlight horizontal " + this.horizontalItem + " " + this.verticalItem);
            }
        }

        private void MoveY(SkeletonPoint leftHand)
        {
            if (this.activePoint.Y - leftHand.Y >= this.verticalDifference / this.verticalSplit)
            {
                //Console.WriteLine(this.activePoint.Y + " " + leftHand.Y);
                this.activePoint.Y = leftHand.Y;
                this.verticalItem++;
                this.activeWindow.highlightButton(this.horizontalItem, this.verticalItem);
                //Console.WriteLine("highlight vertical " + this.horizontalItem + " " + this.verticalItem);
            }
            else if (leftHand.Y - this.activePoint.Y >= this.verticalDifference / this.verticalSplit)
            {
                //Console.WriteLine(this.activePoint.Y + " " + leftHand.Y);
                this.activePoint.Y = leftHand.Y; 
                this.verticalItem--;
                this.activeWindow.highlightButton(this.horizontalItem, this.verticalItem);
                //Console.WriteLine("highlight vertical " + this.horizontalItem + " " + this.verticalItem);
            }
        }

        private void Accept(SkeletonPoint head, SkeletonPoint rightHand, SkeletonPoint leftHand)
        {
            if (!acceptStart) return;
            if (head.Y < rightHand.Y) return;
            if (this.previous > rightHand.Y)
            {
                this.previous = rightHand.Y;
                return;
            }
            acceptStart = false;
            if (setUpMode)
                Setup(leftHand);
            else if(this.activeWindow != null)
            {
                this.activeWindow.Click(this.horizontalItem, this.verticalItem); 
            }
        }
    }
}
